/** @file        bdy_api.c
  * @brief       与百度云服务器交互的接口
  * @details     
  * @author      皓瑞
  * @date        2020-12-28
  * @version     V1.0
  * @copyright   
  **********************************************************************************
  * @attention
  **********************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "baiduyun/bdy_api.h"

using namespace std;
using namespace Json;
using namespace BaiDuYun;

const string Face::access_token_url_ = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials";
const string Face::face_request_url_ = "https://aip.baidubce.com/rest/2.0/face/v3";

/**
 * @brief AccessToken内置curl的回调函数
 * @param ptr 
 * @param size 
 * @param nmemb 
 * @param stream
 * @note  参数详见curl文档定义
 *        https://everything.curl.dev/libcurl/callbacks/write
 */
static size_t access_token_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
    string s((char *) ptr, size * nmemb);              /* 获取到的body存放在ptr中，先将其转换为string格式 */

    bool res;                          /* Json状态码 */
    JSONCPP_STRING errs;               /* 若失败，此string可提供错误信息 */
    Value root;                        /* 建立Json根对象 */
    CharReaderBuilder readerBuilder;   /* 创建 readerBuilder */
    unique_ptr<CharReader> const jsonReader(readerBuilder.newCharReader());  /* 建立jsonReader指针 */

#if BDY_API_DEBUG
    std::cout<<"接受到的json内容为："<<std::endl<<s.c_str()<<std::endl;
#endif

    res = jsonReader->parse(s.c_str(), s.c_str()+s.length(), &root, &errs);
    /*！ 解析Json并提取"access_token"的值 */
    if(res){
        string* access_token_result = static_cast<string*>(stream);
        *access_token_result = root["access_token"].asString();
    }else{
        cout<<"百度云密钥解析失败"<<endl;
    }

    return size * nmemb;
}

/**
 * @brief FaceSearch内置curl的回调函数
 * @param ptr 
 * @param size 
 * @param nmemb 
 * @param stream
 * @note  参数详见curl文档定义
 *        https://everything.curl.dev/libcurl/callbacks/write
 */
static size_t face_search_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
    string* jsonResult = static_cast<string*>(stream);
    *jsonResult = string((char *) ptr, size * nmemb);
    return size * nmemb;
}



/**
 * @brief FaceAdd内置curl的回调函数
 * @param ptr 
 * @param size 
 * @param nmemb 
 * @param stream
 * @note  参数详见curl文档定义
 *        https://everything.curl.dev/libcurl/callbacks/write
 */
static size_t face_add_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
    string* jsonResult = static_cast<string*>(stream);
    *jsonResult = string((char *) ptr, size * nmemb);
    return size * nmemb;
}

/**
 * @brief 一个简单的重载方式用于配置人脸库的设置
 * @param key:需要配置的选项 
 * @note  参数详见百度云人脸API文档定义
 *        https://ai.baidu.com/ai-doc/FACE/7k37c1jfr
 */
Value& Face::operator[] (const string& key)
{
    if(!faceSettings_.isMember(key))
       cout<<"没有此配置!"<<endl;
    
    return faceSettings_[key];


}

/**
 * @brief 用以获取access_token的函数，使用时需要先在百度云控制台申请相应功能的应用，获得对应的API Key和Secret Key
 * @param access_token 获取得到的access token，调用函数时需传入该参数
 * @param AK 应用的API key
 * @param SK 应用的Secret key
 * @return 返回0代表获取access token成功，其他返回值代表获取失败
 */
int Face::AccessToken(string &access_token, const string &AK, const string &SK) {
    
    CURLcode result_code;        /* easy curl状态码 */
    int error_code = 0;
    if (curl) {
        string url = access_token_url_ + "&client_id=" + AK + "&client_secret=" + SK;  /* 拼接URL */
        curl_easy_setopt(curl, CURLOPT_URL, url.data());      /* 设置URL */
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);   /* 不对服务器进行身份验证 */
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);    /* ?? */
        string access_token_result;
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &access_token_result);        /* 设置curl写缓冲 */
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, access_token_callback);   /* 设置curl写回调函数 */
        result_code = curl_easy_perform(curl);                                  /* 发送HTTPS GET */
        if (result_code != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s code number:%d\n",
                    curl_easy_strerror(result_code), result_code) ;
            return 1;
        }
        access_token = access_token_result;
        error_code = 0;
    } else {
        fprintf(stderr, "curl_easy_init() failed.");
        error_code = 1;
    }

    curl_easy_reset(curl);  /* 重置curl */

    return error_code;
}

/**
 * @brief  人脸搜索
 * @param  imageKey:键值对"image"的value,可传入图片的URL或者图像的二进制数据
 *         示image_type而定,默认图像的二进制数据
 * @return true则查找到对应的人脸
 * @note   搜索配置由faceSearchSettings_决定，支持的图像格式为png、jpg、jpeg以及gif
 */
bool Face::FaceSearch(const string& imageKey) {
    string url = face_request_url_ + "/search?access_token=" + access_token_; /* 拼接URL */
    CURLcode result_code;     /* easy curl状态码 */
    int is_success = true;   

    /*! 清空错误缓冲区 */
    curErrorMsg.clear();  

    curl_easy_setopt(curl, CURLOPT_URL, url.data());   /* 设置URL */
    curl_easy_setopt(curl, CURLOPT_POST, 1);           /* 设置请求方式为POST */

    /*! 向html header部添加"Content-Type:application/json;charset=UTF-8" */
    curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    
    /*! 对html的body进行配置 */
    Value faceSearchSettings;
    faceSearchSettings["image_type"] = faceSettings_["image_type"];
    faceSearchSettings["max_face_num"] = faceSettings_["max_face_num"];
    faceSearchSettings["match_threshold"] = faceSettings_["match_threshold"];
    faceSearchSettings["quality_control"] = faceSettings_["quality_control"];
    faceSearchSettings["liveness_control"] = faceSettings_["liveness_control"];
    faceSearchSettings["max_user_num"] = faceSettings_["max_user_num"];
    faceSearchSettings["group_id_list"] = faceSettings_["group_id_list"];
    cout<<faceSearchSettings["image_type"]<<endl;
    if(faceSearchSettings["image_type"] == "BASE64"){
      faceSearchSettings["image"] = Base64::Encode(imageKey.data(), imageKey.size());
    } 
    else if(faceSearchSettings["image_type"] == "URL"){
      faceSearchSettings["image"] = imageKey;
    }
      
    StreamWriterBuilder streamWriterBuilder;
    streamWriterBuilder["indentation"] = "";
    string html_body = writeString( dynamic_cast<StreamWriter::Factory const&>(streamWriterBuilder), faceSearchSettings);   

    /*! 填充body参数 */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, html_body.c_str());
#if BDY_API_DEBUG
    cout<<"body:"<<endl<<html_body.c_str()<<endl<<endl;
#endif
    jsonResult_.clear();
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &jsonResult_);                /* 设置curl写缓冲 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, face_search_callback);    /* 设置curl写回调函数 */

    result_code = curl_easy_perform(curl);
    if (result_code != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(result_code));
        is_success = false;
        return is_success;
    }

    /*! 重置curl的配置 */
    curl_easy_reset(curl);   

#if BDY_API_DEBUG
    std::cout<<"接受到的json内容为："<<std::endl<<jsonResult_.c_str()<<std::endl;
#endif
    
    /*! 判断是否查询到对应人脸 */
    Value faceSearchRes;
    bool res = false;
    CharReaderBuilder readerBuilder;                                         /* 创建 readerBuilder */
    unique_ptr<CharReader> const jsonReader(readerBuilder.newCharReader());  /* 建立jsonReader指针 */
    res  = jsonReader->parse(jsonResult_.c_str(), 
                             jsonResult_.c_str()+jsonResult_.length(),
                             &faceSearchRes, NULL);
    if(!res) is_success = false;
    if(faceSearchRes.isMember("error_code")){
        if(faceSearchRes["error_code"] != 0){
            is_success = false;
            curErrorMsg = faceSearchRes["error_msg"].asString();
        }
    }else{
       is_success = false;
    }

    return is_success;
}

/**
 * @brief  人脸注册
 * @param  imageKey:键值对"image"的value,可传入图片的URL或者图像的二进制数据
 *         示image_type而定,默认图像的二进制数据
 * @return true成功添加对应的人脸
 * @note   搜索配置由faceSearchSettings_决定，支持的图像格式为png、jpg、jpeg以及gif
 */
bool Face::FaceAdd(const std::string& imageKey)
{
    string url = face_request_url_ + "/faceset/user/add?access_token=" + access_token_; /* 拼接URL */
    CURLcode result_code;     /* easy curl状态码 */
    int is_success = true;

    /*! 清空错误缓冲区 */
    curErrorMsg.clear();    

    curl_easy_setopt(curl, CURLOPT_URL, url.data());   /* 设置URL */
    curl_easy_setopt(curl, CURLOPT_POST, 1);           /* 设置请求方式为POST */
    /*! 向html header部添加"Content-Type:application/json;charset=UTF-8" */
    curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /*! 对html的body进行配置 */
    Value faceAddSettings;
    faceAddSettings["image_type"]       = faceSettings_["image_type"];
    faceAddSettings["group_id"]         = faceSettings_["group_id"];
    faceAddSettings["user_id"]          = faceSettings_["user_id"];
    faceAddSettings["user_info"]        = faceSettings_["user_info"];
    faceAddSettings["quality_control"]  = faceSettings_["quality_control"];
    faceAddSettings["liveness_control"] = faceSettings_["liveness_control"];
    faceAddSettings["action_type"]      = faceSettings_["action_type"];
    faceAddSettings["face_sort_type"]   = faceSettings_["face_sort_type"];
    cout<<faceAddSettings["image_type"]<<endl;
    if(faceAddSettings["image_type"] == "BASE64"){
      faceAddSettings["image"] = Base64::Encode(imageKey.data(), imageKey.size());
    } 
    else if(faceAddSettings["image_type"] == "URL"){
      faceAddSettings["image"] = imageKey;
    }

    StreamWriterBuilder streamWriterBuilder;
    streamWriterBuilder["indentation"] = "";
    string html_body = writeString( dynamic_cast<StreamWriter::Factory const&>(streamWriterBuilder), faceAddSettings);

    /*! 填充body参数 */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, html_body.c_str());
#if BDY_API_DEBUG
    cout<<"body:"<<endl<<html_body.c_str()<<endl<<endl;
#endif
    
    jsonResult_.clear();
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &jsonResult_);                /* 设置curl写缓冲 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, face_add_callback);      /* 设置curl写回调函数 */
    
    result_code = curl_easy_perform(curl);
    if (result_code != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(result_code));
        is_success = false;
        return is_success;
    }

    /*! 重置curl的配置 */
    curl_easy_reset(curl);    

    /*! 判断是否查询到对应人脸 */
    Value faceAddRes;
    bool res = false;
    CharReaderBuilder readerBuilder;                                         /* 创建 readerBuilder */
    unique_ptr<CharReader> const jsonReader(readerBuilder.newCharReader());  /* 建立jsonReader指针 */
    res  = jsonReader->parse(jsonResult_.c_str(), 
                             jsonResult_.c_str()+jsonResult_.length(),
                             &faceAddRes, NULL);
    if(!res) is_success = false;
    if(faceAddRes.isMember("error_code")){
        if(faceAddRes["error_code"] != 0){
            is_success = false;
            curErrorMsg = faceAddRes["error_msg"].asString();
        }   
    }else{
       is_success = false;
    }

#if BDY_API_DEBUG
    std::cout<<"接受到的json内容为："<<std::endl<<jsonResult_.c_str()<<std::endl;
#endif
    return is_success;
}

/**
 * @brief  人脸更新
 * @param  imageKey:键值对"image"的value,可传入图片的URL或者图像的二进制数据
 *         示image_type而定,默认图像的二进制数据
 * @return true成功添加对应的人脸
 * @note   搜索配置由faceSearchSettings_决定，支持的图像格式为png、jpg、jpeg以及gif
 *         人脸更新也需要进行活体检测
 */
bool Face::FaceUpdate(const std::string& imageKey)
{
    string url = face_request_url_ + "/faceset/user/update?access_token=" + access_token_; /* 拼接URL */
    CURLcode result_code;     /* easy curl状态码 */
    int is_success = true;

    /*! 清空错误缓冲区 */
    curErrorMsg.clear();    

    curl_easy_setopt(curl, CURLOPT_URL, url.data());   /* 设置URL */
    curl_easy_setopt(curl, CURLOPT_POST, 1);           /* 设置请求方式为POST */
    /*! 向html header部添加"Content-Type:application/json;charset=UTF-8" */
    curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /*! 对html的body进行配置 */
    Value faceUpdateSettings;
    faceUpdateSettings["image_type"]       = faceSettings_["image_type"];
    faceUpdateSettings["group_id"]         = faceSettings_["group_id"];
    faceUpdateSettings["user_id"]          = faceSettings_["user_id"];
    faceUpdateSettings["user_info"]        = faceSettings_["user_info"];
    faceUpdateSettings["quality_control"]  = faceSettings_["quality_control"];
    faceUpdateSettings["liveness_control"] = faceSettings_["liveness_control"];
    faceUpdateSettings["action_type"]      = "REPLACE";
    faceUpdateSettings["face_sort_type"]   = faceSettings_["face_sort_type"];
    cout<<faceUpdateSettings["image_type"]<<endl;
    if(faceUpdateSettings["image_type"] == "BASE64"){
      faceUpdateSettings["image"] = Base64::Encode(imageKey.data(), imageKey.size());
    } 
    else if(faceUpdateSettings["image_type"] == "URL"){
      faceUpdateSettings["image"] = imageKey;
    }

    StreamWriterBuilder streamWriterBuilder;
    streamWriterBuilder["indentation"] = "";
    string html_body = writeString( dynamic_cast<StreamWriter::Factory const&>(streamWriterBuilder), faceUpdateSettings);

    /*! 填充body参数 */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, html_body.c_str());
#if BDY_API_DEBUG
    cout<<"body:"<<endl<<html_body.c_str()<<endl<<endl;
#endif
    
    jsonResult_.clear();
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &jsonResult_);                /* 设置curl写缓冲 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, face_add_callback);      /* 设置curl写回调函数 */
    
    result_code = curl_easy_perform(curl);
    if (result_code != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(result_code));
        is_success = false;
        return is_success;
    }

    /*! 重置curl的配置 */
    curl_easy_reset(curl);    

    /*! 判断是否查询到对应人脸 */
    Value faceUpdateRes;
    bool res = false;
    CharReaderBuilder readerBuilder;                                         /* 创建 readerBuilder */
    unique_ptr<CharReader> const jsonReader(readerBuilder.newCharReader());  /* 建立jsonReader指针 */
    res  = jsonReader->parse(jsonResult_.c_str(), 
                             jsonResult_.c_str()+jsonResult_.length(),
                             &faceUpdateRes, NULL);
    if(!res) is_success = false;
    if(faceUpdateRes.isMember("error_code")){
        if(faceUpdateRes["error_code"] != 0){
            is_success = false;
            curErrorMsg = faceUpdateRes["error_msg"].asString();
        }   
    }else{
       is_success = false;
    }

#if BDY_API_DEBUG == 0
    std::cout<<"接受到的json内容为："<<std::endl<<jsonResult_.c_str()<<std::endl;
#endif
    return is_success;
}

/****************************END OF FILE****************************/